package com.ponhu.ea2.stockcenter.provider.service.biz.stockwarehouse;

import com.alibaba.fastjson.JSON;
import com.ponhu.ea2.stockcenter.dto.WMSEventInfoDTO;
import com.ponhu.ea2.stockcenter.enums.ExchangeActionTypeEnum;
import com.ponhu.ea2.stockcenter.provider.constants.CommonConstant;
import com.ponhu.ea2.stockcenter.provider.support.enums.StockChangeInnerEventEnum;
import com.ponhu.ea2.stockcenter.provider.support.exception.StockRuntimeException;
import com.ponhu.ea2.stockcenter.provider.support.service.RabbitMQService;
import com.ponhu.ea2.stockcenter.provider.support.service.RedisSupportService;
import com.ponhu.ea2.stockcenter.provider.utils.RedisUtils;
import com.ponhu.ea2.stockcenter.vo.ReturnResponseBean;
import io.netty.util.concurrent.FastThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 仓库层库存响应服务
 */
@Slf4j
@Service
public class WarehouseStockResponseService {

    @Autowired // 请求部分失败异常
    protected FastThreadLocal<StockRuntimeException> stockRuntimeExceptionLocal;

    @Autowired
    private FastThreadLocal<String> traceIdLocal;
    @Autowired // 获取请求参数Map(key: 货品编码，value: 仓库变动数量)
    protected FastThreadLocal<Map<String, Integer>> inventoryLocal;
    @Autowired // 当前请求类型对销售侧库存影响的类型枚举
    protected FastThreadLocal<StockChangeInnerEventEnum> saleEventLocal;

    @Autowired
    protected RabbitMQService rabbitMQService;
    @Autowired
    private RedisSupportService redisSupportService;
    @Autowired
    private ConcurrentHashMap<ExchangeActionTypeEnum, AbsCoreHandler> handlerPool;

    // 先加写锁，再在锁内开启事务，这样可以使后抢到锁的线程可以看到先抢到锁的线程提交的事务结果
    public ReturnResponseBean receiveWMSEventHandler(WMSEventInfoDTO eventInfoDTO) {

        // 取消调拨申请、调拨出库、调拨入库需要补充请求参数
        handlerPool.get(eventInfoDTO.getActionType()).preThreadParamInit(eventInfoDTO);

        // 获取分布式读写锁的key值
        List<String> keys = eventInfoDTO.getInventoryInfoList().stream().map(info ->
                        RedisUtils.getRedisKey(CommonConstant.REDISSON_RW_LOCK_STOCK_GLOBAL, info.getInventoryCode()))
                .collect(Collectors.toList());

        ReturnResponseBean returnResponseBean;
        try {
            if (redisSupportService.bucketIsExists(CommonConstant.WITHOUT_REDIS_LOCK)) {
                returnResponseBean = ((WarehouseStockResponseService) AopContext.currentProxy()).handleTransactional(eventInfoDTO);
            } else returnResponseBean = redisSupportService.wrapWithWriteLock(keys, () -> {
                ReturnResponseBean responseBean = ((WarehouseStockResponseService) AopContext.currentProxy()).handleTransactional(eventInfoDTO);

                String traceId = traceIdLocal.get();
                String servletPath = "dubbo facade interface StockForWMSServiceImpl.receiveWMSEventHandler";
                // 若本次出库事件已经是Redis缓存处理，则同步删除请求关联的全部货品编码的出库事件缓存信息
                if (responseBean.isOk()) CompletableFuture.runAsync(() -> {
                    List<ExchangeActionTypeEnum> outEnumList = Arrays.asList(
                            ExchangeActionTypeEnum.SALES_RETURN_CANCLE_OUT,
                            ExchangeActionTypeEnum.PURCHASE_CANCEL_OUT,
                            ExchangeActionTypeEnum.PURCHASE_RETURN_OUT,
                            ExchangeActionTypeEnum.TRANSFER_PLAN_OUT,
                            ExchangeActionTypeEnum.CHECK_LOSS_OUT,
                            ExchangeActionTypeEnum.OTHER_OUT,
                            ExchangeActionTypeEnum.SALES_OUT);
                    if (outEnumList.contains(eventInfoDTO.getActionType()) && !eventInfoDTO.isFirstFrequency()) {
                        log.info("{} ===>> [{}] ===>> 出库事件[Redis缓存处理] ===>> 入参={}",
                                servletPath, traceId, JSON.toJSONString(eventInfoDTO));
                        eventInfoDTO.getInventoryInfoList().stream().forEach(info -> {
                            String redisKey = RedisUtils.getRedisKey(CommonConstant.WMS_OUT_EVENT_HANDLER_ERROR,
                                    eventInfoDTO.getOutWarehouseCode(), info.getInventoryCode());
                            redisSupportService.bucketGetAndDel(redisKey, String.class);
                        });
                    }
                });

                return responseBean;
            });

            List<ExchangeActionTypeEnum> outEnumList = Arrays.asList(
                    ExchangeActionTypeEnum.CHECK_LOSS_OUT,
                    ExchangeActionTypeEnum.OTHER_OUT);
            if (outEnumList.contains(eventInfoDTO.getActionType()) &&
                    ObjectUtils.isNotEmpty(stockRuntimeExceptionLocal.get()))
                throw stockRuntimeExceptionLocal.get();

            // 当前仓库库存变动对销售库存有影响
            if (ObjectUtils.isNotEmpty(saleEventLocal.get())) {
                rabbitMQService.saleStockChangeSendMsg(inventoryLocal.get().keySet());
            }
        } finally {
            saleEventLocal.remove();
            inventoryLocal.remove();
        }

        return returnResponseBean;
    }

    @Transactional(rollbackFor = Exception.class)
    public ReturnResponseBean handleTransactional(WMSEventInfoDTO eventInfoDTO) {

        return handlerPool.get(eventInfoDTO.getActionType()).handle(eventInfoDTO);
    }

}
